"""
Copyright 2007, Thomas Dejanovic.

This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
"""

#  This is the highest level of detail for generating target bus class verilog.
#  Unfortunately it has morphed into a catch all for target bus data.

id = "$Id: hatch_target_bus_verilog.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.2/hatch/hatch_targets/verilog/obsolete/hatch_target_bus_verilog.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])

import re
import math
from hatch_target_bus import Target_Bus


def get_target_bus(hatchling):
    """ Determine the type of target bus to build based on the target bus
        requested by the hatchling.
    """
    targetBus = 0
    if re.match("^.*\s+apb\s+.*$", hatchling.property('target')):   # apb bus.
        targetBus = Target_Bus_apb(hatchling)
    elif re.match("^.*\s+ubus\s+.*$", hatchling.property('target')):    # micro bus between FPGA's.
        targetBus = Target_Bus_ubus(hatchling)
    else:
        raise "*** ERROR - specified target %s in %s is unsupported." % (hatchling.property('target'), hatchling.name)
    return targetBus


# apb slave.
class Target_Bus_apb(Target_Bus):               # TODO XXX Move this into physical classes.
    """ This is the APB Slave target bus class.
    """
    def __init__ (self, hatchling):
        """ """ 
        Target_Bus.__init__(self, hatchling)

        apbMatch  = re.match("^\s*(?P<size>.*)\s*apb\s+slave\s*.*$", self.property('target'))   # XXX WTF ?

        if not apbMatch:
            raise "*** ERROR - apb target format %s in %s could not be parsed." % (self.property('target'), hatchling.name)
        else:
            self.set_property('bus','apb')
            self.dataWidth = 32 

            # address bus width, this can be overridden later but it
            # must be done before any code is generated.
            if re.match("^.*\s*small\s*$", apbMatch.group('size')):
                self.set_property('addr_width', 8)
            elif re.match("^.*\s*medium\s*$", apbMatch.group('size')):
                self.set_property('addr_width', 12)
            elif re.match("^.*\s*large\s*$", apbMatch.group('size')):
                self.set_property('addr_width', 16)
            else:
                self.set_property('addr_width', 30) # full address range. 
                
            # Not mutually exclusive:
            if re.match("^\s*(?P<size>.*)\s*apb\s+slave\s+mux\s*$", self.property('target')) :
                self.set_property('function','mux')
            if re.match("^\s*(?P<size>.*)\s*apb\s+slave\s*$", self.property('target')) :
                self.set_property('function','slave')


    def elaborate (self):
        """ Flesh out data structures for APB Slave as a prelude to generating anything.
        """
        prefix = self.property('prefix')        # get the prefix

        if self.property('bus') == 'apb' :
            self.verilogSetup = "  //- - - - - - - - - - -  setup the apb slave stuff. - - - - - - - - - - - -\n"
            # clocks and reset.
            self.clk        = "%spclk"%(prefix)
            self.clkEn      = "%spclk_en"%(prefix)
            self.gatedClk   = "%sgated_pclk"%(prefix)
            self.gatedClkEn = "%sgated_pclk_en"%(prefix)

            self.add_input_port("", self.clk)
            self.add_input_port("", self.clkEn)
            self.add_input_port("", self.gatedClk)
            self.add_input_port("", self.gatedClkEn)

            # change reset to be active high because [ soap box monologue deleted ].
            self.reset      = "preset"

            self.add_input_port("", "%spreset_l"%(prefix))
            self.add_wire("", self.reset)
            self.verilogSetup += """
  // make an active high reset.
  assign preset  = ~%spreset_l;
"""%(prefix)

            # address bus width.
            if self.property('function') == 'mux' :
                self.addrWidth = self.property('addr_width')
            if self.property('function') == 'slave' :
                # calculate the number of bits to actualy use in the
                # address decode. This assumes that the base address for
                # every module is 0 and high order address decode is
                # handled by another module up the hierachy. 
                self.addrRange = self.hatchling.get_address_range()
                self.addrWidth = int(math.ceil(math.log(self.addrRange) / math.log(2.0))) - 2

            self.addrWidthString = "[%d:2]"%(self.addrWidth + 1)

            self.addr       = "%spaddr"%(prefix)
            self.writeData  = "%spwdata"%(prefix)
            self.writeEn    = "%spwrite"%(prefix)
            self.readData   = "%sprdata"%(prefix)
            self.readEn     = ""

            self.add_input_port("", "%spenable"%(prefix))
            self.add_input_port("", "%spsel"%(prefix))
            self.add_input_port(self.addrWidthString, self.addr)
            self.add_input_port("[31:0]",             self.writeData)
            self.add_input_port("",                   self.writeEn)
            self.add_output_port("[31:0]",            self.readData)

            if self.property('function') == 'slave' :
                
                # pipeline address decode, so change the signals used.
                self.writeEn    = "pwrite_en"
                self.readData   = "prdata_int"
                self.readEn     = "pread_en"

                self.add_wire("", self.writeEn)
                self.add_reg("[31:0]", self.readData)
                self.add_wire("", self.readEn)
                self.add_reg("",        "pread_en_int")
                self.add_reg("",        "pwrite_en_int")

                self.verilogSetup += """            
  //
  // psel will be a combinatorial decode of the upper address bits so
  // we should register it here to make the timing a little easier and
  // remove a multicycle path (apb is a 2 cycle access with address
  // set up in the first cycle). No need to make the physical design
  // harder than it needs to be  :-)
  //
  always @ (posedge %s) if (%s) begin
    if (%s) begin
"""%(self.gatedClk, self.gatedClkEn, self.reset)

                self.verilogSetup += """
      pread_en_int  <= 1'd0;
      pwrite_en_int <= 1'd0;
    end else begin
      pread_en_int  <= %spsel & ~%spwrite;
      pwrite_en_int <= %spsel &  %spwrite;
    end
  end
"""%(prefix, prefix, prefix, prefix)

                self.verilogSetup += """
  assign pread_en  = %spenable & pread_en_int;
  assign pwrite_en = %spenable & pwrite_en_int;
  assign %sprdata  = pread_en ? %s : 32'd0;
"""%(prefix, prefix, prefix, self.readData)

                # tail the verilogSetup string.
                self.verilogSetup += """

  //- - - - - - - - - - - end of apb slave setup. - - - - - - - - - - -
"""


# ubus slave.
class Target_Bus_ubus(Target_Bus) :
    """ This is the UBUS (micro bus) slave target bus class.  the ubus is a
        simple request / ack based bus with 16 bit data and address ports.
    """
    def __init__ (self, hatchling):
        """ """
        Target_Bus.__init__(self, hatchling)
        ubusMatch  = re.match("^\s*(?P<size>.*)\s*ubus\s+slave\s*.*$", self.property('target'))

        if not ubusMatch :
            raise "*** ERROR - ubus target format %s in %s could not be parsed."%(self.property('target'),
        else:       
            self.set_property('bus','ubus')
            self.dataWidth = 16

            # address bus width, this can be overridden later but it
            # must be done before any code is generated.
            if re.match("^.*\s*small\s*$", ubusMatch.group('size')):
                self.set_property('addr_width', 4)
            elif re.match("^.*\s*medium\s*$", ubusMatch.group('size')):
                self.set_property('addr_width', 8)
            elif re.match("^.*\s*large\s*$", ubusMatch.group('size')):
                self.set_property('addr_width', 12)
            else:
                # full address range. 
                self.set_property('addr_width', 16)
                
            if re.match("^\s*(?P<size>.*)\s*ubus\s+slave\s+mux\s*$", self.property('target')) :
                self.set_property('function','mux')
            if re.match("^\s*(?P<size>.*)\s*ubus\s+slave\s*$", self.property('target')) :
                self.set_property('function','slave')

    def elaborate (self):
        """ Flesh out data structures for UBUS Slave as a prelude to generating anything."""
        # get the prefix
        prefix = self.property('prefix')

        if self.property('bus') == 'ubus' :
            self.verilogSetup = "  //- - - - - - - - - - -  setup the ubus slave stuff. - - - - - - - - - - - -\n"

            # clocks and reset.
            self.clk        = "%sub_clk"%(prefix)
            self.clkEn      = "%sub_clk_en"%(prefix)
            self.gatedClk   = "%sgated_ub_clk"%(prefix)
            self.gatedClkEn = "%sgated_ub_clk_en"%(prefix)
            self.reset      = "%sub_reset"%(prefix)

            self.add_input_port("", self.clk)
            self.add_input_port("", self.clkEn)
            self.add_input_port("", self.gatedClk)
            self.add_input_port("", self.gatedClkEn)
            self.add_input_port("", self.reset)

            # address bus width.
            if self.property('function') == 'mux' :
                self.addrWidth = self.property('addr_width')

            if self.property('function') == 'slave' :
                # calculate the number of bits to actualy use in the
                # address decode. This assumes that the base address for
                # every module is 0 and high order address decode is
                # handled by another module up the hierachy. 
                self.addrRange = self.hatchling.get_address_range()
                self.addrWidth = int(math.ceil(math.log(self.addrRange) / math.log(2.0))) - 2

            self.addrWidthString = "[%d:1]"%(self.addrWidth)

            self.addr       = "%sub_addr"%(prefix)
            self.writeData  = "%sub_write_data"%(prefix)
            self.writeEn    = "%sub_write"%(prefix)
            self.readData   = "%sub_read_data"%(prefix)
            self.readEn     = "%sub_read"%(prefix)

            self.add_output_port("", "%sub_ack"%(prefix))
            self.add_input_port(self.addrWidthString, self.addr)
            self.add_input_port("[15:0]",             self.writeData)
            self.add_input_port("",                   self.writeEn)
            self.add_output_port("[15:0]",            self.readData)
            self.add_input_port("",                   self.readEn)

            if self.property('function') == 'slave' :
                
                # pipeline address decode, so change the signals used.
                self.writeEn    = "write_en" # a pulse.
                self.readEn     = "read_en" # a level.
                self.readData   = "read_data_int"

                self.add_wire("", self.writeEn)
                self.add_reg("", self.readEn)
                self.add_reg("", "write_en_int")
                self.add_reg("", "write_en_int_d")
                self.add_wire("", "%sub_ack"%(prefix));
                self.add_reg("[15:0]", self.readData)

                self.verilogSetup += """            
  //
  // read and write will be a combinatorial decode of the upper
  // address bits so register them here to make the timing a 
  // little easier
  //
  always @ (posedge %s) if (%s) begin
    if (%s) begin
"""%(self.gatedClk, self.gatedClkEn, self.reset)

                self.verilogSetup += """
      read_en        <= 1'd0;
      write_en_int   <= 1'd0;
      write_en_int_d <= 1'd0;
    end else begin
      read_en        <= %sub_read;
      write_en_int   <= %sub_write;
      write_en_int_d <= write_en_int;
    end
  end
"""%(prefix, prefix)

                self.verilogSetup += """  
  assign write_en = write_en_int & ~write_en_int_d;
  assign %sub_ack = read_en | write_en_int;
  assign %sub_read_data = read_en ? %s : 16'd0;
"""%(prefix, prefix, self.readData)

                # tail the verilogSetup string.
                self.verilogSetup += """

  //- - - - - - - - - - - end of ubus slave setup. - - - - - - - - - - -
"""


